/*
 * Exploder
 * 
 * Tero Pyylampi (218437), Jussi Tuurinkoski (211594)
 *
 * An engine for updating all the physic objects in the game
 */

#include "GamePhysicObjectEngine.h"
#include "GamePhysicObject.h"
#include "elements/GameGround.h"
#include "elements/GameElementWoodBox.h"
#include "elements/GameElementSteelBox.h"
#include "elements/GameElementSteelBar.h"
#include "bombs/GameBomb.h"
#include "bombs/GameBombSmallMine.h"
#include "bombs/GameBombMedium.h"
#include "borders/GameBorderWood.h"
#include "../../GameCamera.h"

// default constructor
GamePhysicObjectEngine::GamePhysicObjectEngine() : GameAbstractEngine( ENGINE_RUNNING ) {
	mPhysicObjects = new std::vector< GamePhysicObject* >();
}

// default destructor
GamePhysicObjectEngine::~GamePhysicObjectEngine() {
	for( unsigned int i = 0; i < mPhysicObjects->size(); ++i )
		delete mPhysicObjects->at( i );
	delete mPhysicObjects;
}

// update engine state
void GamePhysicObjectEngine::update( cocos2d::ccTime dt ) {
	for( unsigned int i = 0; i < mPhysicObjects->size(); ++i ) {
		mPhysicObjects->at( i )->update( dt );
		if( mPhysicObjects->at( i )->canBeRemovedFromTheGame() ) {
			delete mPhysicObjects->at( i );
			mPhysicObjects->erase( mPhysicObjects->begin() + i );
			--i;
		}
	}
}

// creates the ground object
void GamePhysicObjectEngine::createGroundObject( b2World *world, cocos2d::CCLayer *layer ) {
	GameGround *ground = new GameGround( world, layer );
	mPhysicObjects->push_back( ground );
}

// create building element
GameConstructionElement *GamePhysicObjectEngine::createElementObject( b2World *world, cocos2d::CCLayer *layer, GameCamera *camera, bool editingMode, PhysicObjectItems type, b2Vec2 pos, float rotation, ElementType properties ) {
	switch( type ) {
	case ELEMENT_WOOD_BOX:
		{
			GameElementWoodBox *woodBox = new GameElementWoodBox( world, layer, camera, editingMode, pos, properties, rotation );
			mPhysicObjects->push_back( woodBox );
			return woodBox;
			break;
		}		

	case ELEMENT_STEEL_BOX:
		{
			GameElementSteelBox *steelBox = new GameElementSteelBox( world, layer, camera, editingMode, pos, properties, rotation);
			mPhysicObjects->push_back( steelBox );
			return steelBox;
			break;
		}

	case ELEMENT_STEEL_BAR:
		{
			GameElementSteelBar *steelBar = new GameElementSteelBar( world, layer, camera, editingMode, pos, properties, rotation );
			mPhysicObjects->push_back( steelBar );
			return steelBar;
			break;
		}
	default:
		{
			break;
		}
	}

	// should not reach here
	return 0;
}

// create bomb
GameBomb *GamePhysicObjectEngine::createBombObject( b2World *world, cocos2d::CCLayer *layer, GameCamera *camera, PhysicObjectItems type, b2Vec2 pos ){
	switch( type ) {
	case BOMB_SMALL_MINE: 
		{
			GameBomb *smallMine = new GameBombSmallMine( world, layer, camera, pos );
			mPhysicObjects->push_back( smallMine );
			return smallMine;
		}
	case BOMB_MEDIUM:
		{
			GameBomb *mediumBomb = new GameBombMedium( world, layer, camera, pos );
			mPhysicObjects->push_back( mediumBomb );
			return mediumBomb;
		}
	}

	// should not reach here
	return 0;
}

// create border object
GameBorder *GamePhysicObjectEngine::createBorderObject( b2World *world, cocos2d::CCLayer *layer, GameCamera *camera, PhysicObjectItems type, b2Vec2 pos ) {
	switch( type ) {
	case BORDER_WOOD:
		GameBorder *woodBorder = new GameBorderWood( world, layer, camera, pos );
		mPhysicObjects->push_back( woodBorder );
		return woodBorder;
	}

	// should not reach here
	return 0;
}


// get physic objects
std::vector< GamePhysicObject* > *GamePhysicObjectEngine::getPhysicObjects() {
	return mPhysicObjects;
}

// find nearest physic object
GamePhysicObject *GamePhysicObjectEngine::findNearestPhysicObject( b2Vec2 worldLoc ) {
	float nearestDistance = 99999.0f;
	GamePhysicObject *nearestObject = 0;

	for( unsigned int i = 0; i < mPhysicObjects->size(); ++i ) {
		float length = mPhysicObjects->at( i )->calculateDistanceTo( worldLoc );
		if( length < nearestDistance ) {
			nearestDistance = length;
			nearestObject = mPhysicObjects->at( i );
		}
	}

	return nearestObject;
}

// find the object which owns this specific body
GamePhysicObject *GamePhysicObjectEngine::findOwnerOfBody( b2Body *body ) {
	for( unsigned int i = 0; i < mPhysicObjects->size(); ++i ) {
		if( mPhysicObjects->at( i )->ownerOfBody( body ) )
			return mPhysicObjects->at( i );
	}

	// should not reach here as every object should be owned by a physic object
	return 0;
}

// check whether specific object collides with any of the existing objects
bool GamePhysicObjectEngine::checkIfObjectCollidesWithAnything( b2Body *body ) {
	b2ContactEdge *e = body->GetContactList();
	
	// check for contacts
	while( e != 0 ) {
		if( e->contact->IsTouching() )
			return true;
		e = e->next;
	}

	// not touching anything
	return false;
}
